<template>
  <div class="flight-simulation-container">
    <!-- 飞行控制组件 -->
    <div class="flight-control" :class="{ expanded: isPanelExpanded }">
      <!-- 控制图标 -->
      <div class="control-icon" @click="togglePanel" v-if="!isPanelExpanded" title="模拟飞行">
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <path d="M22 12h-4l-3 9L9 3l-3 9H2"></path>
        </svg>
      </div>

      <!-- 飞行控制面板 -->
      <div class="flight-control-panel" v-if="isPanelExpanded">
        <div class="panel-header">
          <h3>模拟飞行</h3>
          <div class="close-btn" @click="isPanelExpanded = false">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <path d="M18 6L6 18M6 6l12 12"></path>
            </svg>
          </div>
        </div>
        <div class="control-buttons">
          <button class="control-btn start-btn" @click="startFlight" :disabled="isFlying">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <polygon points="5 3 19 12 5 21 5 3"></polygon>
            </svg>
            开始
          </button>
          <button class="control-btn pause-btn" @click="pauseFlight" :disabled="!isFlying">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <rect x="6" y="4" width="4" height="16"></rect>
              <rect x="14" y="4" width="4" height="16"></rect>
            </svg>
            暂停
          </button>
          <button class="control-btn reset-btn" @click="resetFlight">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <path d="M21 2v6h-6M3 10V4a2 2 0 012-2h6M3 22v-6h6M21 14v6a2 2 0 01-2 2h-6"></path>
            </svg>
            重置
          </button>
        </div>
        <div class="speed-control">
          <label>飞行速度: {{ flightSpeed }}x</label>
          <input type="range" v-model.number="flightSpeed" min="0.5" max="3" step="0.1" class="speed-slider">
        </div>
        <div class="camera-control">
          <label class="checkbox-label">
            <input type="checkbox" v-model="cameraFollow">
            <span class="checkmark"></span>
            相机跟随
          </label>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
import * as Cesium from 'cesium'
const props = defineProps({
  viewer: {
    type: Object,
    required: true
  }
});

// 状态管理
const isFlying = ref(false);
const flightSpeed = ref(1);
const cameraFollow = ref(true);
const airplaneEntity = ref(null);
const flightEntityPath = ref(null);
const flightPath = ref([]);
const isPanelExpanded = ref(false);
let animationFrameId = null;
let isPaused = false;
let cameraFollowHandler = null

// 切换面板展开/折叠状态
const togglePanel = () => {
  isPanelExpanded.value = !isPanelExpanded.value;
  if (!isPanelExpanded.value) {
    //清空
    clearAll();
  } else {
    //初始化
    initFlightPath();
    loadAirplaneModel();
  }
};

// 初始化飞行路径（示例路径：从北京到上海）
const initFlightPath = () => {
  // 航线：北京大兴机场 (PKX) -> 上海虹桥机场 (SHA)
  // 时间以秒为单位，包含起飞、爬升、巡航、下降与着陆阶段
  // 大兴机场中心坐标 (approx): 39.5091 N, 116.4086 E
  // 虹桥机场中心坐标 (approx): 31.1975 N, 121.3366 E
  flightPath.value = [
    // 0s: 大兴跑道待命 / 地面
    { lon: 116.4086, lat: 39.5091, alt: 50, time: 0 },
    // 20s: 起飞并爬升
    { lon: 116.4200, lat: 39.5105, alt: 500, time: 20 },
    // 120s: 初始爬升段
    { lon: 117.5000, lat: 38.8000, alt: 6000, time: 140 },
    // 900s: 接近巡航高度
    { lon: 118.8000, lat: 36.5000, alt: 11000, time: 900 },
    // 1800s: 巡航
    { lon: 120.0000, lat: 34.0000, alt: 11000, time: 1800 },
    // 2400s: 开始下降
    { lon: 121.0000, lat: 32.0000, alt: 6000, time: 2400 },
    // 2560s: 进近低空
    { lon: 121.2500, lat: 31.2500, alt: 800, time: 2560 },
    // 2600s: 虹桥跑道/着陆
    { lon: 121.3366, lat: 31.1975, alt: 60, time: 2600 }
  ];

  // 如果 viewer 可用，则绘制一条可见的航迹（polyline）以便观察轨迹
  try {
    if (props.viewer) {
      // 移除旧的航迹实体
      if (flightEntityPath.value) {
        try { props.viewer.entities.remove(flightEntityPath.value) } catch (e) { }
        flightEntityPath.value = null
      }

      const positions = flightPath.value.map(p => Cesium.Cartesian3.fromDegrees(p.lon, p.lat, p.alt));
      flightEntityPath.value = props.viewer.entities.add({
        name: 'flight-path-polyline',
        polyline: {
          positions,
          width: 3,
          material: new Cesium.PolylineDashMaterialProperty({
            color: Cesium.Color.CYAN.withAlpha(0.9)
          }),
          clampToGround: false
        }
      });
    }
  } catch (e) {
    // 不要抛出异常，记录以便调试
    // console.warn('initFlightPath: failed to add polyline', e)
  }
};

// 加载飞机模型
const loadAirplaneModel = () => {
  if (!props.viewer) return;

  // 移除现有飞机实体
  if (airplaneEntity.value) {
    props.viewer.entities.remove(airplaneEntity.value);
    airplaneEntity.value = null;
  }

  // 获取起始点位置
  const startPoint = flightPath.value[0];
  const startPosition = Cesium.Cartesian3.fromDegrees(startPoint.lon, startPoint.lat, startPoint.alt);

  // 创建飞机实体
  airplaneEntity.value = props.viewer.entities.add({
    name: '模拟飞机',
    position: startPosition,
    model: {
      uri: new URL('../assets/models/airplane.glb', import.meta.url).href,
      scale: 1, // 增大模型缩放，确保可见
      minimumPixelSize: 128, // 增大最小像素大小
      maximumScale: 256,
      silhouetteColor: Cesium.Color.WHITE,
      silhouetteSize: 2,
      // 确保模型可见
      show: true
    }
  });

  // 直接设置相机位置，确保能看到飞机 - 拉远视角
  props.viewer.camera.setView({
    destination: Cesium.Cartesian3.fromDegrees(startPoint.lon, startPoint.lat, startPoint.alt + 30000),
    orientation: {
      heading: Cesium.Math.toRadians(0),
      pitch: Cesium.Math.toRadians(-90), // 更平缓的视角
      roll: 0
    }
  });
};

// 开始飞行
const startFlight = () => {
  if (!props.viewer || !airplaneEntity.value) return;

  isFlying.value = true;
  isPaused = false;

  // 重置时钟
  props.viewer.clock.currentTime = Cesium.JulianDate.now();
  props.viewer.clock.multiplier = flightSpeed.value;
  props.viewer.clock.shouldAnimate = true;

  // 构建平滑的 SampledPositionProperty：为每秒生成采样点并使用 Hermite 插值
  const positionProperty = new Cesium.SampledPositionProperty();

  // 计算总飞行时间（秒）
  const totalFlightTime = flightPath.value[flightPath.value.length - 1].time;

  // 如果总时长为0则退出
  if (totalFlightTime <= 0) {
    console.warn('飞行路径总时长无效')
    return
  }

  // Helper: 在两个经纬高点之间按比例插值（线性经纬度/高度插值，足够用于生成密采样）
  const interpolatePoint = (p0, p1, t) => {
    return {
      lon: p0.lon + (p1.lon - p0.lon) * t,
      lat: p0.lat + (p1.lat - p0.lat) * t,
      alt: p0.alt + (p1.alt - p0.alt) * t
    }
  }

  // 为每一秒生成样本，保证平滑且避免大跨度跳变
  for (let sec = 0; sec <= totalFlightTime; sec++) {
    // 找到 sec 所在的路径段
    let segIndex = 0
    while (segIndex < flightPath.value.length - 1 && sec > flightPath.value[segIndex + 1].time) {
      segIndex++
    }

    const segStart = flightPath.value[segIndex]
    const segEnd = flightPath.value[Math.min(segIndex + 1, flightPath.value.length - 1)]
    const segDuration = Math.max(1, segEnd.time - segStart.time)
    const localT = segDuration === 0 ? 0 : (sec - segStart.time) / segDuration

    const interp = interpolatePoint(segStart, segEnd, Math.min(Math.max(localT, 0), 1))
    const time = Cesium.JulianDate.addSeconds(props.viewer.clock.currentTime, sec, new Cesium.JulianDate())
    const position = Cesium.Cartesian3.fromDegrees(interp.lon, interp.lat, interp.alt)
    positionProperty.addSample(time, position)
  }

  // 使用 Hermite 插值并设置较低阶数以获得平滑且稳定的轨迹
  positionProperty.setInterpolationOptions({
    interpolationDegree: 2,
    interpolationAlgorithm: Cesium.HermitePolynomialApproximation
  });

  // 更新飞机实体属性
  airplaneEntity.value.position = positionProperty;
  airplaneEntity.value.orientation = new Cesium.VelocityOrientationProperty(positionProperty);

  // 开启定制相机跟随（基于飞机朝向，让相机与飞行方向保持一致）
  if (props.viewer) {
    enableCameraFollow()
  }

  // 开始动画循环（用于其他每帧任务）；相机跟随已交给 trackedEntity
  animate();
};

// 暂停飞行
const pauseFlight = () => {
  isFlying.value = false;
  isPaused = true;

  if (props.viewer) {
    props.viewer.clock.shouldAnimate = false;
  }

  if (animationFrameId) {
    cancelAnimationFrame(animationFrameId);
    animationFrameId = null;
  }
  // 取消相机跟随
  disableCameraFollow()
};

// 重置飞行
const resetFlight = () => {
  pauseFlight();

  if (airplaneEntity.value && props.viewer) {
    // 重置时钟
    props.viewer.clock.currentTime = Cesium.JulianDate.now();
    props.viewer.clock.multiplier = flightSpeed.value;

    // 重置飞机位置到起始点
    const startPoint = flightPath.value[0];
    const startPosition = Cesium.Cartesian3.fromDegrees(startPoint.lon, startPoint.lat, startPoint.alt + 30000);

    // 直接设置位置，不使用property
    airplaneEntity.value.position = startPosition;
    airplaneEntity.value.orientation = Cesium.Quaternion.IDENTITY;

    // 重置相机位置 - 与初始设置一致（拉远以便能看到飞机模型）
    props.viewer.camera.flyTo({
      destination: Cesium.Cartesian3.fromDegrees(startPoint.lon, startPoint.lat, startPoint.alt + 30000),
      orientation: {
        heading: Cesium.Math.toRadians(0),
        pitch: Cesium.Math.toRadians(-30),
        roll: 0
      },
      duration: 2
    });
  }
};

// 动画循环
const animate = () => {
  if (!isFlying.value || isPaused || !props.viewer || !airplaneEntity.value) return;

  // 现在相机跟随由 viewer.trackedEntity 管理，因此这里只保留循环的基础职责（未来可扩展）
  animationFrameId = requestAnimationFrame(animate);
};

// 启用基于飞机朝向的相机跟随：在 preRender 阶段执行，保持相机与飞机朝向一致并位于飞机后上方
const enableCameraFollow = () => {
  if (!props.viewer || !airplaneEntity.value || cameraFollowHandler) return
  const scene = props.viewer.scene
  cameraFollowHandler = function () {
    try {
      const current = props.viewer.clock.currentTime
      const position = airplaneEntity.value.position && airplaneEntity.value.position.getValue(current)
      const orientation = airplaneEntity.value.orientation && airplaneEntity.value.orientation.getValue(current)
      if (!position || !orientation) return

      // 将四元数转换为 HeadingPitchRoll，用 heading 表示飞机朝向
      const hpr = Cesium.HeadingPitchRoll.fromQuaternion(orientation)
      const heading = hpr.heading || 0
      // 我们希望相机位于飞机后方观察飞机前进方向，因此将相机 heading 设置为飞机 heading + PI
      const cameraHeading = heading + Math.PI

      // 基准俯仰：向下看，使用较大的负角度以保证能看到前方地面（例如 -30°）
      // 同时允许飞机自身俯仰对相机俯仰有小幅影响（乘以系数）
      const basePitch = -Cesium.Math.toRadians(30)
      const pitchInfluence = (typeof hpr.pitch === 'number' ? hpr.pitch : 0) * 0.2
      const cameraPitch = basePitch + pitchInfluence

      // 根据飞机高度动态设置距离（range）
      // 提高最小距离以避免在低空/起飞阶段相机过于靠近模型
      const carto = Cesium.Cartographic.fromCartesian(position)
      const height = (carto && carto.height) ? carto.height : 10000
      // 对高度使用更保守的放大倍率，并设置较大的最小值
      const calculated = height * 1.8
      const range = Math.max(4000, Math.min(120000, calculated))

      // 使用 camera.lookAt 在飞机位置上以 HeadingPitchRange 控制相机
      props.viewer.camera.lookAt(position, new Cesium.HeadingPitchRange(cameraHeading, cameraPitch, range))
    } catch (e) {
      // 不要抛出以免中断渲染循环
      // 记录以便调试
      // console.warn('cameraFollowHandler error', e)
    }
  }
  scene.preRender.addEventListener(cameraFollowHandler)
}

const disableCameraFollow = () => {
  if (!props.viewer || !cameraFollowHandler) return
  try {
    props.viewer.scene.preRender.removeEventListener(cameraFollowHandler)
  } catch (e) {
    // ignore
  }
  cameraFollowHandler = null
  // 恢复 camera 到默认（取消 lookAtTransform 以清除任何剩余跟随变换）
  try {
    props.viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
  } catch (e) {
    // ignore
  }
  // 清除 trackedEntity（以防先前被设置）
  try {
    props.viewer.trackedEntity = undefined
  } catch (e) { }
}

// 监听飞行速度变化
watch(flightSpeed, (newSpeed) => {
  if (props.viewer) {
    props.viewer.clock.multiplier = newSpeed;
  }
});

// 监听相机跟随开关，开启或关闭自定义跟随
watch(cameraFollow, (val) => {
  if (!props.viewer) return
  if (val) {
    enableCameraFollow()
  } else {
    disableCameraFollow()
  }
})
const clearAll = () => {
  pauseFlight();
  if (airplaneEntity.value && props.viewer) {
    props.viewer.entities.remove(airplaneEntity.value);
    airplaneEntity.value = null;
  }
  if (flightEntityPath.value && props.viewer) {
    try {
      props.viewer.entities.remove(flightEntityPath.value);
    } catch (e) { }
    flightEntityPath.value = null;
  }
}
// 组件挂载时初始化
onMounted(() => {
  
});

// 组件卸载时清理
onBeforeUnmount(() => {
  clearAll();
});
</script>

<style scoped>
.flight-simulation-container {
  position: absolute;
  top: 20px;
  right: 20px;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
}

.flight-control {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
  transition: all 0.3s ease;
}

.control-icon {
  width: 32px;
  height: 32px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow:
    0 8px 32px rgba(102, 126, 234, 0.4),
    0 0 0 1px rgba(255, 255, 255, 0.1);
  transition: all 0.3s ease;
  color: white;
}

.control-icon:hover {
  transform: translateY(-2px);
  box-shadow:
    0 12px 40px rgba(102, 126, 234, 0.6),
    0 0 0 1px rgba(255, 255, 255, 0.2);
}

.flight-control-panel {
  background: linear-gradient(135deg, rgba(15, 23, 42, 0.15) 0%, rgba(30, 41, 59, 0.15) 100%);
  backdrop-filter: blur(20px);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 10px;
  padding: 12px;
  min-width: 200px;
  opacity: 0;
  transform: translateY(20px) scale(0.95);
  pointer-events: none;
  transition: all 0.3s ease;
  box-shadow:
    0 10px 25px rgba(0, 0, 0, 0.15),
    0 0 0 1px rgba(255, 255, 255, 0.08);
}

.flight-control.expanded .flight-control-panel {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: all;
}

.flight-control-panel h3 {
  margin: 0 0 12px 0;
  font-size: 16px;
  color: white;
  font-weight: 600;
  letter-spacing: 0.5px;
  text-align: center;
  padding-bottom: 10px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.15);
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
  padding-bottom: 10px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.15);
}

.panel-header h3 {
  margin: 0;
  color: white;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0.5px;
  padding-bottom: 0;
  border-bottom: none;
}

.close-btn {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: rgba(255, 255, 255, 0.7);
  transition: all 0.2s ease;
}

.close-btn:hover {
  background: rgba(255, 255, 255, 0.1);
  color: white;
}

.control-buttons {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 12px;
}

.control-btn {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px 10px;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 6px;
  color: rgba(255, 255, 255, 0.95);
  font-size: 11px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
}

.control-btn:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.2);
  transform: translateX(2px);
}

.control-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
}

.control-btn.start-btn:hover:not(:disabled) {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-color: rgba(255, 255, 255, 0.2);
  color: white;
  box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}

.control-btn.pause-btn:hover:not(:disabled) {
  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
  border-color: rgba(255, 255, 255, 0.2);
  color: white;
  box-shadow: 0 4px 15px rgba(245, 87, 108, 0.3);
}

.control-btn.reset-btn:hover:not(:disabled) {
  background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
  border-color: rgba(255, 255, 255, 0.2);
  color: white;
  box-shadow: 0 4px 15px rgba(79, 172, 254, 0.3);
}

.speed-control {
  margin-bottom: 12px;
}

.speed-control label {
  display: block;
  margin-bottom: 8px;
  font-size: 11px;
  color: rgba(255, 255, 255, 0.9);
}

.speed-slider {
  width: 100%;
  height: 4px;
  -webkit-appearance: none;
  appearance: none;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 2px;
  outline: none;
  transition: background 0.2s ease;
}

.speed-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 50%;
  cursor: pointer;
  transition: all 0.2s ease;
  box-shadow: 0 2px 6px rgba(102, 126, 234, 0.3);
  border: 2px solid rgba(255, 255, 255, 0.8);
}

.speed-slider::-webkit-slider-thumb:hover {
  transform: scale(1.1);
  box-shadow: 0 3px 8px rgba(102, 126, 234, 0.5);
}

.speed-slider::-moz-range-thumb {
  width: 16px;
  height: 16px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 50%;
  cursor: pointer;
  transition: all 0.2s ease;
  box-shadow: 0 2px 6px rgba(102, 126, 234, 0.3);
  border: 2px solid rgba(255, 255, 255, 0.8);
}

.speed-slider::-moz-range-thumb:hover {
  transform: scale(1.1);
  box-shadow: 0 3px 8px rgba(102, 126, 234, 0.5);
}

.camera-control {
  margin-top: 12px;
}

.checkbox-label {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 6px;
  color: rgba(255, 255, 255, 0.95);
  cursor: pointer;
  transition: all 0.2s ease;
  font-size: 11px;
  font-weight: 500;
}

.checkbox-label:hover {
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.2);
}

.checkbox-label input {
  display: none;
}

.checkmark {
  width: 18px;
  height: 18px;
  border: 2px solid rgba(255, 255, 255, 0.3);
  border-radius: 4px;
  position: relative;
  transition: all 0.2s ease;
}

.checkbox-label input:checked+.checkmark {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-color: transparent;
}

.checkbox-label input:checked+.checkmark::after {
  content: '';
  position: absolute;
  left: 5px;
  top: 2px;
  width: 4px;
  height: 8px;
  border: solid white;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
</style>